#pragma once

#include <string>
#include "vector2.h"
#include "Polygon.h"
#include "Collision.h"

using namespace std;

class GameObject {
public:
	GameObject(const Vector2 &pos_ = Vector2(0.0f,0.0f),
			   const Vector2 &scale_ = Vector2(1.0f,1.0f),
			   const float &rot_ = 0.0f,
			   const Vector2 &vel_ = Vector2(0.0f,0.0f),
			   const float &rotvel_ = 0.0f,
			   const Vector2 &accel_ = Vector2(0.0f,0.0f),
			   const float &rotaccel_ = 0.0f);

	GameObject(GameObject &rhs);
	~GameObject();

	GameObject & operator=(GameObject &rhs);
	
	float distanceFrom(GameObject *obj);
	///< Simple quick check to determine if two objects are within each other's max radius
	bool sameRegion(GameObject *obj);

	Vector2 & getScale() {return scale;}
	Vector2 & getPos() {return pos;}
	Vector2 & getLastPos() {return lastpos;}
	float getRadius() {return sqrt(scale.x * scale.x + scale.y * scale.y) / 2;}
	float & getRot() {return rot;}
	float & getLastRot() {return lastrot;}
	Vector2 & getVel() {return vel;}
	float & getRotVel() {return rotvel;}
	Vector2 & getAccel() {return accel;}
	float & getRotAccel() {return rotaccel;}
	Poly & getPoly() {return poly;}
	Vector2 & getPoint(int i) {return poly[i];}
	float & getMass() {return mass;}
	float getInverseMass(){return mass > 0.0001f ? 1 / mass : 0.0f;}
	float & getInertia() {return inertia;}
	float getInverseInertia() {return (inertia > 0.0001f) ? 1.0f / (inertia * mass) : 0.0f;}
	float & getElasticity() {return elasticity;}
	int & getNumPoints() {return poly.size;}
	bool & getFlipped() {return flipped;}
	std::string & getTexture(int i) {return textures[i];}

	float left() {return poly[0].x;}
	float right() {return poly[1].x;}
	float top() {return poly[0].y;}
	float bottom() {return poly[2].y;}

	virtual void setScale(Vector2 & newscale);
	virtual void setPos(Vector2 & newpos);
	void setRot(float newrot);
	void setVel(Vector2 & newvel)  { vel = newvel; }
	void setRotVel(float newrotvel) { rotvel = newrotvel; }
	void setAccel(Vector2 & newaccel) {accel = newaccel;}
	void setRotAccel(float newrotaccel) {rotaccel = newrotaccel;}
	void setFlipped(bool flipped_) {flipped = flipped_;}
	void setColorFilter(int i, std::string color) {colorFilters[i] = Zeni::get_Colors()[color];}
	void setTexture(int i, std::string texture) { textures[i] = texture; }
	void addTexture(std::string texture) { textures.push_back(texture); colorFilters.push_back(Zeni::Color()); }
	
	virtual void update(const float &timestep); ///< Update the position, rotation, velocity, and rotational velocity.
	void render();

protected:
	bool flipped;
	std::vector<string> textures;
	std::vector<Zeni::Color> colorFilters;
	/*Zeni::Color colorFilter;
	std::string filteredTexture, nonFilteredTexture;*/
	Poly poly;
	Vector2 centerofmass; ///< Point defining object's center of mass

	Vector2 scale;		///< Scale of texture
	Vector2 vel;		///< Velocity of object
	Vector2 accel;		///< Acceleration of object
	Vector2 pos; 		///< Position of object
	Vector2 lastpos;	///< Previous position of object
	float rot;			///< Rotation of object
	float lastrot;		///< Previous rotation of object
	float rotvel;		///< Rotational velocity of object
	float rotaccel;		///< Rotational acceleration of object
	float mass;			///< Mass of object
	float inertia;		///< Inertia of object
	float elasticity;	///< Elasticity of object
};

struct Collision
{
	Collision();
	
	///< Determines if collision either has occured or will occur within dt time
	Collision(GameObject* a, GameObject* b, float dt);

	static bool objectsIntersect(GameObject* a, GameObject* b)
	{
		Collision c(a, b, 0);
		return c.m_collisionReported;
	}

	static void applyResponse(GameObject* a, GameObject* b, float dt, bool rigid = false)
	{
		Collision c(a, b, dt);
		if (c.m_collisionReported){
			c.applyResponse(dt, rigid);
		}
	}

	void seperateBodies();
	void unseperateBodies();
	void applyResponse(float dt, bool rigid = false);

	bool			m_collisionReported;
	GameObject*		m_object[2];
	Poly			m_poly[2];
	Vector2			m_ncoll;
	Vector2			m_mtd;
	float			m_tcoll;
	CollisionInfo	m_collisionInfo;
	ContactInfo		m_contacts;
	Vector2			m_vcoll;
};